package com.ruoyi.common.utils.file;


import com.ruoyi.common.config.LlmInfoConfig;
import com.ruoyi.common.utils.oss.OssUtils;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageTree;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.extractor.WordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class PDFUtils {
    // 文件保存的路径
    public static final String filePath = LlmInfoConfig.getProfile() + File.separator + "file";
    // 文件本地保存路径
    public static final String localFilePath = LlmInfoConfig.getProfile() + File.separator + "temp";
    /**
     * 定义一个正则表达式模式，用于匹配文档中的目录部分
     * 此模式不区分大小写，能够匹配中文的"目录"、英文的"Table of Contents"以及繁体中文的"目錄"
     */
    private static final Pattern TOC_PATTERN = Pattern.compile("目录|Table of Contents|目錄", Pattern.CASE_INSENSITIVE);

    public static String getFilePath() {
        return filePath;
    }

    public static String getLocalFilePath() {
        return localFilePath;
    }

    /**
     * 读取Docx文件内容
     * 该方法接收一个multipart文件作为输入，尝试读取其内容并以字符串形式返回
     * 主要通过Apache POI库来解析Docx文件，提取其中的文本内容
     *
     * @param file 要读取的Docx文件，作为multipart形式上传
     * @return 返回读取到的文件文本内容
     * @throws IOException 如果在读取文件过程中发生I/O错误，则抛出此异常
     */
    public static String readDocxFile(MultipartFile file) throws IOException {
        // 尝试打开文件输入流，确保文件内容能被读取
        try (InputStream inputStream = file.getInputStream()) {
            // 创建XWPFDocument对象来解析Docx文件
            XWPFDocument document = new XWPFDocument(inputStream);
            // 获取文件中的所有段落
            List<XWPFParagraph> paragraphs = document.getParagraphs();

            // 将所有段落的文本内容提取出来，并用换行符连接，形成完整的文档文本
            return paragraphs.stream()
                    .map(XWPFParagraph::getText)
                    .collect(Collectors.joining("\n"));
        } catch (Exception e) {
            return readDocFile(file);
        }
    }

    /**
     * 读取Doc文件内容
     * 该方法接收一个multipart文件作为输入，尝试读取其内容并以字符串形式返回
     * 主要通过Apache POI库来解析Docx文件，提取其中的文本内容
     *
     * @param file 要读取的Docx文件，作为multipart形式上传
     * @return 返回读取到的文件文本内容
     * @throws IOException 如果在读取文件过程中发生I/O错误，则抛出此异常
     */
    public static String readDocFile(MultipartFile file) throws IOException {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("上传的文件为空");
        }
        try (InputStream inputStream = file.getInputStream()) {
            // 使用 HWPFDocument 来解析 .doc 文件
            HWPFDocument document = new HWPFDocument(inputStream);
            WordExtractor extractor = new WordExtractor(document);
            //这个地方会报Cannot invoke "org.apache.poi.hwpf.model.ListTables.getLfo(int)" because "listTables" is null，暂时不用管。
            return extractor.getText();
        } catch (IOException e) {
            throw new IOException("读取文件时发生错误: " + e.getMessage(), e);
        }
    }

    /**
     * 从输入流中读取 PDF 文件内容并转换为文本
     *
     * @param inputStream 输入流，用于读取 PDF 文件
     * @return PDF 文件的文本内容，如果读取过程中发生错误则返回空字符串
     */
    public static String getPdfContentText(InputStream inputStream) {

        if (inputStream == null) {
            throw new IllegalArgumentException("Input stream cannot be null");
        }

        try (FileInputStream pdfStream = (FileInputStream) inputStream;) {

            byte[] pdfBytes = pdfStream.readAllBytes();
            // 1. 使用 PDFBox 读取 PDF 内容
            try (PDDocument pdfDocument = Loader.loadPDF(pdfBytes);) {

                // 2. 创建 PDFTextStripper 对象用于提取 PDF 文本内容
                PDFTextStripper pdfTextStripper = new PDFTextStripper();

                // 3. 使用 PDFTextStripper 提取 PDF 文档中的文本
                String pdfText = pdfTextStripper.getText(pdfDocument);
                return pdfText;
            }

        } catch (IOException e) {
            // 4. 捕获并处理 IOException 异常
            e.printStackTrace();
            System.out.println("pdo-to-docx:error" + e.getMessage());
            return "";
        }
    }

    /**
     * 从 PDF 文件中提取文本内容
     *
     * @param pdfPath PDF 文件的路径
     * @return PDF 文件的文本内容，如果发生错误则返回空字符串
     */
    public static String getPdfContentText(String pdfPath) {

        // 尝试使用指定路径打开 PDF 文件流
        // 1. 使用 PDFBox 读取 PDF 内容
        try (PDDocument pdfDocument = Loader.loadPDF(new File(pdfPath));) {


            // 2. 创建 PDFTextStripper 实例，用于提取 PDF 文本内容
            PDFTextStripper pdfTextStripper = new PDFTextStripper();

            // 3. 提取 PDF 文本内容并返回
            String pdfText = pdfTextStripper.getText(pdfDocument);
            return pdfText;
        } catch (IOException e) {
            // 打印异常信息并返回空字符串
            e.printStackTrace();
            System.out.println("pdo-to-docx:error" + e.getMessage());
            return "";
        }
    }

    /**
     * 将PDF文件转换为Markdown格式文本
     *
     * @param pdfFilePath PDF文件的路径
     * @param fileName    PDF文件的名称
     * @param fileName1   PDF文件的备用名称，用于特定情况下的文件命名
     * @param serverUrl   服务器的URL，用于构建图片的完整路径
     * @param type        指示是否使用备用文件名和路径的类型
     * @return 转换后的Markdown文件路径
     * @throws IOException 如果文件读写过程中发生错误
     */
    public static String convertPdfToMarkdown(String pdfFilePath, String fileName, String fileName1, String serverUrl,
                                              int type) throws IOException {

        // 创建File对象以加载PDF文件
        File file = new File(pdfFilePath);

        // 加载PDF文档
        PDDocument document = Loader.loadPDF(file);

        // PDFTextStripper pdfStripper = new PDFTextStripper();

        // 构建Markdown文件的保存路径
        String mdFilePath = filePath + fileName + "_md";

        // 创建路径
        PDFUtils.createDirectoryIfNotExists(mdFilePath);

        String imageSavePath = "";

        // 根据type参数决定文件和图片的保存路径和名称
        if (type == 1) {
            mdFilePath += "/" + fileName1 + ".md";
            imageSavePath = filePath + fileName + "_img" + "/" + fileName1 + "/";
            serverUrl += "/" + fileName + "_img" + "/" + fileName1;
        } else {
            mdFilePath += "/" + fileName + ".md";
            imageSavePath = filePath + fileName + "_img/";
            serverUrl += "/" + fileName + "_img";
        }

        // 创建路径
        PDFUtils.createDirectoryIfNotExists(imageSavePath);

        System.out.println("mdFilePath:String : " + mdFilePath);

        // 自定义PDFTextStripper
        CustomPDFTextStripper pdfStripper = new CustomPDFTextStripper();

        // 读取PDF内容
        String pdfText = pdfStripper.getText(document);
        System.out.println("pdfText : " + pdfText);

        // 提取图片并保存到服务器本地
        Map<String, String> imageMap = extractAndSaveImages(document, imageSavePath);

        // 将PDF内容转换为Markdown格式，并替换图片URL
        String markdownText = convertTextToMarkdown(pdfText, imageMap, serverUrl);

        // 保存Markdown文件
        Files.write(Paths.get(mdFilePath + "/"), markdownText.getBytes());

        // 关闭PDF文档
        document.close();

        // 返回Markdown文件的路径
        return mdFilePath;
    }

    /**
     * 获取PDF中的图片，并且保存到指定路径
     *
     * @param document      文档对象
     * @param imageSavePath 图片保存路径
     * @throws IOException
     */
    private static Map<String, String> extractAndSaveImages(PDDocument document, String imageSavePath) throws IOException {
        Map<String, String> imageMap = new HashMap<>();
        int imageCounter = 0;

        for (PDPage page : document.getPages()) {
            PDResources resources = page.getResources();
            for (COSName cosName : resources.getXObjectNames()) {
                if (resources.isImageXObject(cosName)) {
                    PDImageXObject image = (PDImageXObject) resources.getXObject(cosName);
                    String imageFileName = "image_" + imageCounter + ".png";
                    File imageFile = new File(imageSavePath, imageFileName);
                    ImageIO.write(image.getImage(), "png", imageFile);
                    imageMap.put("image_" + imageCounter, imageFileName);
                    imageCounter++;
                }
            }
        }

        return imageMap;
    }

    /**
     * 将PDF内容转换为md文件，并替换图片里面的URL
     *
     * @param text      pdf 内容
     * @param imageMap  图片
     * @param serverUrl 替换图片的url
     * @return
     */
    private static String convertTextToMarkdown(String text, Map<String, String> imageMap, String serverUrl) {

        String markdownText = text.replaceAll("\n", "  \n");

        // 使用正则表达式识别图片引用并替换为Markdown格式的图片链接
        Pattern pattern = Pattern.compile("\\[img:(\\d+)\\]");
        Matcher matcher = pattern.matcher(markdownText);

        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String imageIndex = matcher.group(1);
            String imageFileName = imageMap.get("image_" + imageIndex);
            if (imageFileName != null) {
                String imageUrl = serverUrl + "/" + imageFileName;
                matcher.appendReplacement(sb, "![" + imageFileName + "](" + imageUrl + ")");
            }
        }
        matcher.appendTail(sb);

        return sb.toString();
    }

    /**
     * 拆分pdf
     *
     * @param inputStream 文件流
     * @return 返回拆分文件的路径
     * @throws IOException
     */
    public static List<String> splitPdfByChapters(InputStream inputStream, String fileName) throws IOException {

        // 加载PDF文档
        PDDocument document = Loader.loadPDF(inputStream.readAllBytes());

        // 创建PDF文本剥离器
        PDFTextStripper pdfStripper = new PDFTextStripper();

        // 拼接输出目录路径
        String outputDir = filePath + fileName;
        // 确保输出目录存在
        PDFUtils.createDirectoryIfNotExists(outputDir);

        // 获取PDF的总页数
        int totalPages = document.getNumberOfPages();
        System.out.println("totalPages" + totalPages);

        // 查找目录页码
        List<Integer> tocPages = findTocPages(document, totalPages);
        System.out.println("tocPages" + tocPages.toString());

        // 解析目录，获取章节信息
        List<ChapterInfo> chapters = parseToc(document, tocPages);
        System.out.println("size : " + chapters.size());
        System.out.println("chapters" + chapters.toString());

        // 存储拆分后的文件路径
        List<String> filePath = new ArrayList<>();

        // 用于记录当前拆分的章节页码
        int pageNums = 0;

        // 按章节拆分PDF
        for (int i = 0; i < chapters.size(); i++) {

            ChapterInfo chapter = chapters.get(i);
            System.out.println("chapter : " + chapter.toString());

            // 获取当前章节的起始页和结束页
            int startPage = chapter.getStartPage();
            int endPage = (i < chapters.size() - 1) ? chapters.get(i + 1).getStartPage() - 1 : totalPages;

            // 输出章节页码信息
            System.out.println("startPage : " + startPage);
            System.out.println("endPage : " + endPage);

            // 设置文本剥离器的起始页和结束页
            pdfStripper.setStartPage(startPage);
            pdfStripper.setEndPage(endPage);

            // 创建一个新的PDF文档用于存储当前章节的内容
            PDDocument chapterDoc = new PDDocument();
            // 将相应页码的页面添加到新文档中
            for (int pageNum = startPage; pageNum <= endPage; pageNum++) {
                chapterDoc.addPage(document.getPage(pageNum - 1));
            }
            // 更新章节页码计数
            ++pageNums;
            // 保存章节PDF
            String chapterFileName = String.format("%s/2_%d_Page_%d_to_%d.pdf", outputDir, pageNums, startPage,
                    endPage);
            filePath.add(chapterFileName);
            File outputFile = new File(chapterFileName);
            // 确保输出文件的父目录存在
            if (!outputFile.getParentFile().exists()) {
                outputFile.getParentFile().mkdirs();
            }
            // 保存章节文档并关闭
            chapterDoc.save(outputFile);
            chapterDoc.close();
        }
        // 关闭原始文档
        document.close();

        // 返回拆分后的文件路径列表
        return filePath;
    }

    /**
     * 拆分pdf（本地版）
     *
     * @param inputStream 文件流
     * @return 返回拆分文件的路径
     * @throws IOException
     */
    public static List<String> localSplitPdfByChapters(InputStream inputStream, String fileName) throws IOException {

        // 加载PDF文档
        PDDocument document = Loader.loadPDF(inputStream.readAllBytes());

        // 创建PDF文本剥离器
        PDFTextStripper pdfStripper = new PDFTextStripper();

        // 拼接输出目录路径
        String outputDir = localFilePath + "/" + fileName;// 更改为本地输出目录

        // 确保输出目录存在
        PDFUtils.createDirectoryIfNotExists(outputDir);

        // 获取PDF的总页数
        int totalPages = document.getNumberOfPages();
        System.out.println("totalPages" + totalPages);

        // 查找目录页码
        List<Integer> tocPages = findTocPages(document, totalPages);
        System.out.println("tocPages" + tocPages.toString());

        // 解析目录，获取章节信息
        List<ChapterInfo> chapters = parseToc(document, tocPages);
        System.out.println("size : " + chapters.size());
        System.out.println("chapters" + chapters.toString());

        // 存储拆分后的文件路径
        List<String> filePath = new ArrayList<>();

        // 用于记录当前拆分的章节页码
        int pageNums = 0;

        // 按章节拆分PDF
        for (int i = 0; i < chapters.size(); i++) {

            ChapterInfo chapter = chapters.get(i);
            System.out.println("chapter : " + chapter.toString());

            // 获取当前章节的起始页和结束页
            int startPage = chapter.getStartPage();
            int endPage = (i < chapters.size() - 1) ? chapters.get(i + 1).getStartPage() - 1 : totalPages;

            // 输出章节页码信息
            System.out.println("startPage : " + startPage);
            System.out.println("endPage : " + endPage);

            // 设置文本剥离器的起始页和结束页
            pdfStripper.setStartPage(startPage);
            pdfStripper.setEndPage(endPage);

            // 创建一个新的PDF文档用于存储当前章节的内容
            PDDocument chapterDoc = new PDDocument();
            // 将相应页码的页面添加到新文档中
            for (int pageNum = startPage; pageNum <= endPage; pageNum++) {
                chapterDoc.addPage(document.getPage(pageNum - 1));
            }
            // 更新章节页码计数
            ++pageNums;
            // 保存章节PDF
            String chapterFileName = String.format("%s/2_%d_Page_%d_to_%d.pdf", outputDir, pageNums, startPage,
                    endPage);
            filePath.add(chapterFileName);
            File outputFile = new File(chapterFileName);
            // 确保输出文件的父目录存在
            if (!outputFile.getParentFile().exists()) {
                outputFile.getParentFile().mkdirs();
            }
            // 保存章节文档并关闭
            chapterDoc.save(outputFile);
            chapterDoc.close();
        }
        // 关闭原始文档
        document.close();

        // 返回拆分后的文件路径列表
        return filePath;
    }

    /**
     * 在给定的PDF文档中查找目录页
     *
     * @param document   PDF文档对象，用于提取文本和信息
     * @param totalPages 文档的总页数，用于确定搜索范围
     * @return 返回一个包含目录页页码的列表
     * @throws IOException 如果在处理PDF文档时发生I/O错误
     */
    public static List<Integer> findTocPages(PDDocument document, int totalPages) throws IOException {
        // 初始化一个列表，用于存储目录页的页码
        List<Integer> tocPages = new ArrayList<>();
        // 创建一个PDFTextStripper对象，用于从PDF文档中提取文本
        PDFTextStripper pdfStripper = new PDFTextStripper();

        // 遍历文档的每一页，寻找目录页
        for (int pageNum = 1; pageNum <= totalPages; pageNum++) {
            // 设置当前处理的页码范围为单页
            pdfStripper.setStartPage(pageNum);
            pdfStripper.setEndPage(pageNum);

            // 提取当前页的文本内容
            String pageText = pdfStripper.getText(document);
            // 使用正则表达式匹配目录页的特征
            Matcher matcher = TOC_PATTERN.matcher(pageText);
            // 如果当前页包含目录的特征，则将页码添加到列表中
            if (matcher.find()) {
                tocPages.add(pageNum);
            }
        }

        // 打印目录页的列表
        System.out.println("tocPages" + tocPages.toString());
        // 返回目录页的列表
        return tocPages;
    }

    /**
     * 解析目录页，生成章节信息列表
     *
     * @param document PDF文档对象
     * @param tocPages 目录页码列表
     * @return 章节信息列表
     * @throws IOException 如果读取PDF文档时发生错误
     */
    private static List<ChapterInfo> parseToc(PDDocument document, List<Integer> tocPages) throws IOException {

        List<ChapterInfo> chapters = new ArrayList<>();

        System.out.println(tocPages.toString());

        // 自定义PDFTextStripper
        CustomPDFTextStripper pdfStripper = new CustomPDFTextStripper();

        // 正则表达式匹配章节信息
        Pattern pattern = Pattern.compile("(\\s*)(.*?)\\s*(\\d+)\\s*$");
        // Pattern pattern = Pattern.compile(".*?(\\d+)\\s*$");

        for (int tocPage : tocPages) {

            pdfStripper.setStartPage(tocPage);
            pdfStripper.setEndPage(tocPage);

            String tocText = pdfStripper.getText(document);

            //System.out.println("TOC Page " + tocPage + ": " + tocText); // 打印目录页内容

            String[] lines = tocText.split("\n");

            for (String line : lines) {
                Matcher matcher = pattern.matcher(line);
                if (matcher.find()) {
                    try {
                        int level = matcher.group(1).length(); // 根据缩进确定级别
                        String title = matcher.group(2);
                        int pageNumber = Integer.parseInt(matcher.group(3)) - 1;
                        chapters.add(new ChapterInfo(title, pageNumber, level));
                        System.out.println("ChapterInfo : " + new ChapterInfo(title, pageNumber, level)); // 打印解析的章节信息
                    } catch (NumberFormatException e) {
                        System.err.println("Invalid page number: " + matcher.group(3));
                    }
                }
            }
        }

        // 选择level最大的章节
        int maxLevel = chapters.stream().mapToInt(ChapterInfo::getLevel).max().orElse(0);
        System.out.println("maxLevel:" + maxLevel);
        List<ChapterInfo> maxLevelChapters = new ArrayList<>();
        for (ChapterInfo chapter : chapters) {
            //System.out.println("Chapter Level: " + chapter.getLevel() + ", Title: " + chapter.getTitle());
            if (chapter.getLevel() == maxLevel) {
                maxLevelChapters.add(chapter);
            }
        }

        // 如果第一个章节的起始页码大于1，则添加一个起始页码为1的章节
        if (!maxLevelChapters.isEmpty() && maxLevelChapters.get(0).getStartPage() > 1) {
            maxLevelChapters.add(0, new ChapterInfo("", 1, 1));
        }

        return maxLevelChapters;
    }

    /**
     * 将文本中的Markdown图片链接转换为新的Markdown格式图片链接
     * 此方法主要用于处理从文本in中提取的Markdown格式图片链接，并将其转换为新的Markdown格式图片链接
     * 新的图片链接是通过调用OssFileUpload.upload方法上传图片到OSS（对象存储服务）后得到的
     *
     * @param text 包含Markdown图片链接的文本
     * @return 替换后的文本，其中的Markdown图片链接指向新的位置
     * @throws Exception 如果在处理过程中发生错误，例如网络请求失败或文件上传失败
     */
    public static String convertTextToMarkdownSaveImage(String text) throws Exception {
        // 定义正则表达式匹配Markdown格式的图片链接，这里按textin返回的图片链接以"https?://web-api.textin.com"开头的
        Pattern pattern = Pattern.compile("\\!\\[.*?\\]\\((https?://web-api.textin.com[^\\s]+)\\)");
        Matcher matcher = pattern.matcher(text);
        StringBuffer result = new StringBuffer();

        // 遍历所有匹配的图片链接
        while (matcher.find()) {
            // 获取原始的图片链接，并替换其中的反斜杠为斜杠，以统一URL格式
            String oldUrl = matcher.group(1).replace("\\", "/");
            System.err.println("oldUrl : " + oldUrl);

            // 获取文件扩展名，用于后续的文件上传
            String extension = FileUtils.getFileExtension(oldUrl);

            // 调用OSS文件上传方法，将原始链接的文件上传到OSS，并获取新的文件链接
            String newUrl = OssUtils.upload(extension, oldUrl);
            System.err.println("newUrl : " + newUrl);
            // 使用新的图片链接替换原始的Markdown图片链接
            matcher.appendReplacement(result, "![](" + newUrl + ")");
        }
        // 将剩余的文本追加到 result 中
        matcher.appendTail(result);

        // 返回替换后的文本
        return result.toString();
    }

    /**
     * （本地版）
     * 将文本中的Markdown图片链接转换为新的Markdown格式图片链接
     * 此方法主要用于处理从文本in中提取的Markdown格式图片链接，并将其转换为新的Markdown格式图片链接
     * 新的图片链接是通过调用OssFileUpload.upload方法上传图片到OSS（对象存储服务）后得到的
     *
     * @param text 包含Markdown图片链接的文本
     * @return 替换后的文本，其中的Markdown图片链接指向新的位置
     * @throws Exception 如果在处理过程中发生错误，例如网络请求失败或文件上传失败
     */
    public static String localConvertTextToMarkdownSaveImage(String text) throws Exception {
        // 定义正则表达式匹配Markdown格式的图片链接，这里按textin返回的图片链接以"https?://web-api.textin.com"开头的
        Pattern pattern = Pattern.compile("\\!\\[.*?\\]\\((https?://web-api.textin.com[^\\s]+)\\)");
        Matcher matcher = pattern.matcher(text);
        StringBuffer result = new StringBuffer();

        // 遍历所有匹配的图片链接
        while (matcher.find()) {
            // 获取原始的图片链接，并替换其中的反斜杠为斜杠，以统一URL格式
            String oldUrl = matcher.group(1).replace("\\", "/");
            System.err.println("oldUrl : " + oldUrl);

            // 获取文件扩展名，用于后续的文件上传
            String extension = FileUtils.getFileExtension(oldUrl);

            // 调用OSS文件上传方法，将原始链接的文件上传到OSS，并获取新的文件链接
            String newUrl = OssUtils.upload(extension, oldUrl);
            System.err.println("newUrl : " + newUrl);
            // 使用新的图片链接替换原始的Markdown图片链接
            matcher.appendReplacement(result, "![](" + newUrl + ")");
        }
        // 将剩余的文本追加到 result 中
        matcher.appendTail(result);

        // 返回替换后的文本
        return result.toString();
    }

    /**
     * 删除文档中图片链接
     *
     * @param text 待处理的文本
     * @return 删除图片链接后的文本
     * @throws IllegalArgumentException 如果输入文本为 null
     */
    public static String deleteImage(String text) {
        if (text == null) {
            throw new IllegalArgumentException("输入文本不能为空");
        }
        if (text.isEmpty()) {
            return text;
        }

        Matcher matcher = Pattern.compile("\\!\\[[^\\]]*\\]\\((https?://web-api\\.textin\\.com[^\\s]+)\\)").matcher(text);
        StringBuffer result = new StringBuffer();

        // 遍历所有匹配的图片链接并删除
        while (matcher.find()) {
            matcher.appendReplacement(result, "");
        }
        matcher.appendTail(result);

        return result.toString();
    }

    /**
     * 读取Markdown文件的内容
     *
     * @param inputStream Markdown文件的输入流
     * @return 文件内容的字符串表示
     * @throws IOException 如果文件不存在或读取过程中发生错误
     */
    public static String readMarkdownFile(InputStream inputStream) throws IOException {

        // File file = new File(filePaths);

        // 检查文件是否存在
        // if (!file.exists()) {
        // throw new IOException("File does not exist: " + filePaths);
        // }

        StringBuilder content = new StringBuilder();

        // 使用BufferedReader读取文件内容到StringBuilder中
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }
        }

        // 返回文件内容的字符串表示
        return content.toString();
    }

    /**
     * 根据给定的目录路径创建目录如果它尚不存在
     * 此方法旨在确保指定的目录存在，以便在文件操作之前避免潜在的FileNotFoundException
     * 它首先检查目录是否存在，如果不存在，则创建它
     *
     * @param directoryPath 目录的路径如果目录不存在，将创建此路径的目录
     * @throws IOException 如果创建目录时发生I/O错误
     */
    public static void createDirectoryIfNotExists(String directoryPath) throws IOException {

        // 获取目录路径
        Path path = Paths.get(directoryPath);

        // 检查路径是否存在
        if (!Files.exists(path)) {
            // 创建路径
            Files.createDirectories(path);
            System.out.println("Directory created: " + directoryPath);
        } else {
            System.out.println("Directory already exists: " + directoryPath);
        }
    }

    /**
     * 拆分pdf
     *
     * @param pdfName 文件路径
     * @return 返回拆分文件的路径
     * @throws IOException
     */
    public List<String> splitPdfByChapters(String pdfName, String fileName) throws IOException {

        PDDocument document = Loader.loadPDF(new File(filePath + pdfName));
        PDFTextStripper pdfStripper = new PDFTextStripper();

        // System.out.println("pdfText " + pdfStripper.getText(document));

        String outputDir = filePath + fileName;
        this.createDirectoryIfNotExists(outputDir);

        // 获取PDF的总页数
        int totalPages = document.getNumberOfPages();
        System.out.println("totalPages " + totalPages);

        // 查找目录页码
        List<Integer> tocPages = findTocPages(document, totalPages);
        System.out.println("tocPages " + tocPages);

        // 解析目录，获取章节信息
        List<ChapterInfo> chapters = parseToc(document, tocPages);
        System.out.println("size : " + chapters.size());
        System.out.println("tocPages" + chapters.toString());

        List<String> filePath = new ArrayList<>();

        try {

            int pageNums = 0;

            // 按章节拆分PDF
            for (int i = 0; i < chapters.size(); i++) {

                ChapterInfo chapter = chapters.get(i);
                System.out.println("chapter : " + chapter.toString());

                int startPage = chapter.getStartPage();

                int endPage = (i < chapters.size() - 1) ? chapters.get(i + 1).getStartPage() - 1 : totalPages;

                if (endPage >= startPage) {
                    System.out.println("startPage : " + startPage);
                    System.out.println("endPage : " + endPage);

                    pdfStripper.setStartPage(startPage);
                    pdfStripper.setEndPage(endPage);

                    PDDocument chapterDoc = new PDDocument();
                    for (int pageNum = startPage; pageNum <= endPage; pageNum++) {
                        chapterDoc.addPage(document.getPage(pageNum - 1));
                    }
                    ++pageNums;
                    // 保存章节PDF
                    String chapterFileName = String.format("%s/" + pdfName + "_%d_Page_%d_to_%d.pdf", outputDir,
                            pageNums, startPage, endPage);
                    filePath.add(chapterFileName);
                    File outputFile = new File(chapterFileName);
                    if (!outputFile.getParentFile().exists()) {
                        outputFile.getParentFile().mkdirs();
                    }
                    chapterDoc.save(outputFile);
                    chapterDoc.close();
                }
            }
        } catch (Exception e) {
            System.out.println("error" + e.getMessage());
        }

        document.close();

        return filePath;
    }

    /**
     * 按照章节拆分PDF
     *
     * @param inputStream
     * @param fileName
     * @return
     * @throws IOException
     */
    public List<String> splitPdfByBookmarks(InputStream inputStream, String fileName) throws IOException {

        List<String> pathList = new ArrayList<>();

        String outputDir = filePath + fileName;
        this.createDirectoryIfNotExists(outputDir);

        try (PDDocument document = Loader.loadPDF(inputStream.readAllBytes())) {
            PDDocumentOutline outline = document.getDocumentCatalog().getDocumentOutline();
            if (outline == null) {
                throw new IOException("No bookmarks found in the PDF.");
            }

            List<PDOutlineItem> bookmarks = new ArrayList<>();
            PDOutlineItem root = outline.getFirstChild();
            while (root != null) {
                bookmarks.add(root);
                root = root.getNextSibling();
            }

            int startPage = 0;
            for (PDOutlineItem bookmark : bookmarks) {
                PDPageDestination destination = (PDPageDestination) bookmark.getDestination();
                if (destination == null) {
                    continue;
                }

                int endPage = document.getPages().indexOf(destination.getPage());
                if (startPage == endPage) {
                    continue;
                }

                PDDocument splitDocument = new PDDocument();
                PDPageTree pages = document.getPages();
                for (int i = startPage; i <= endPage; i++) {
                    splitDocument.addPage(pages.get(i));
                }

                String outputFileName = outputDir + File.separator + bookmark.getTitle() + ".pdf";
                splitDocument.save(outputFileName);
                pathList.add(outputFileName);
                splitDocument.close();

                startPage = endPage + 1;
            }
        }

        return pathList;
    }

    /**
     * 将pdf转成md
     *
     * @param inputStream pdf路径
     * @param fileName    存放文件路径md
     * @param serverUrl   服务器路径
     * @throws IOException
     */
    public String convertPdfToMarkdown(InputStream inputStream, String fileName, String fileName1, String serverUrl,
                                       int type) throws IOException {

        // File file = new File(pdfFilePath);

        PDDocument document = Loader.loadPDF(inputStream.readAllBytes());

        // PDFTextStripper pdfStripper = new PDFTextStripper();

        String mdFilePath = filePath + fileName + "_md";

        // 创建路径
        this.createDirectoryIfNotExists(mdFilePath);

        String imageSavePath = "";

        if (type == 1) {
            mdFilePath += "/" + fileName1 + ".md";
            imageSavePath = filePath + fileName + "_img" + "/" + fileName1 + "/";
            serverUrl += "/" + fileName + "_img" + "/" + fileName1;
        } else {
            mdFilePath += "/" + fileName + ".md";
            imageSavePath = filePath + fileName + "_img/";
            serverUrl += "/" + fileName + "_img";
        }

        // 创建路径
        this.createDirectoryIfNotExists(imageSavePath);

        System.out.println("mdFilePath:String : " + mdFilePath);

        // 自定义PDFTextStripper
        CustomPDFTextStripper pdfStripper = new CustomPDFTextStripper();

        // 读取PDF内容
        String pdfText = pdfStripper.getText(document);
        System.out.println("pdfText : " + pdfText);

        // 提取图片并保存到服务器本地
        Map<String, String> imageMap = extractAndSaveImages(document, imageSavePath);

        // 将PDF内容转换为Markdown格式，并替换图片URL
        String markdownText = convertTextToMarkdown(pdfText, imageMap, serverUrl);

        // 保存Markdown文件
        Files.write(Paths.get(mdFilePath + "/"), markdownText.getBytes());

        document.close();

        return mdFilePath;
    }

    /**
     * 拆分pdf的方法 废弃
     *
     * @param pdfName        文件路径
     * @param outputDir      输出路径
     * @param maxSizeInBytes 按多少大小进行拆分
     * @throws IOException
     */
    public List<String> splitPdfBySize(String pdfName, String outputDir, long maxSizeInBytes) throws IOException {

        List<String> filePaths = new ArrayList<>();

        try (PDDocument document = Loader.loadPDF(new File(filePath + pdfName))) {

            outputDir += filePath + outputDir;
            this.createDirectoryIfNotExists(outputDir);

            Iterator<PDPage> pageIterator = document.getPages().iterator();
            long currentSize = 0;
            int partNumber = 1;
            PDDocument currentDocument = new PDDocument();

            while (pageIterator.hasNext()) {
                PDPage page = pageIterator.next();
                long pageSize = getPageSize(page);

                if (currentSize + pageSize > maxSizeInBytes) {
                    // Save the current document if it has pages
                    if (currentDocument.getNumberOfPages() > 0) {
                        saveDocument(currentDocument, outputDir, partNumber);
                        partNumber++;
                    }

                    // Start a new document
                    currentDocument = new PDDocument();
                    currentSize = 0;
                }

                // Add the page to the current document
                currentDocument.addPage(page);
                currentSize += pageSize;
            }

            // Save the last document if it has pages
            if (currentDocument.getNumberOfPages() > 0) {
                String outputFileName = saveDocument(currentDocument, outputDir, partNumber);
                filePaths.add(outputFileName);
            }
        }

        return filePaths;
    }

    /**
     * 获取文件的页数
     *
     * @param page
     * @return
     * @throws IOException
     */
    private long getPageSize(PDPage page) throws IOException {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            PDDocument tempDocument = new PDDocument();
            tempDocument.addPage(page);
            tempDocument.save(baos);
            tempDocument.close();
            return baos.size();
        }
    }

    /**
     * 转存为Doc文档文件
     *
     * @param document
     * @param outputDir
     * @param partNumber
     * @return
     * @throws IOException
     */
    private String saveDocument(PDDocument document, String outputDir, int partNumber) throws IOException {
        String outputFileName = outputDir + File.separator + "part_" + partNumber + ".pdf";
        document.save(outputFileName);
        document.close();
        return outputFileName;
    }

    /**
     * 读取md或txt文件格式内容
     *
     * @param filePaths 文件路径
     * @return fileContent String
     * @throws IOException
     */
    public String readMarkdownFile(String filePaths) throws IOException {

        File file = new File(filePaths);

        // 检查文件是否存在
        if (!file.exists()) {
            throw new IOException("File does not exist: " + filePaths);
        }

        StringBuilder content = new StringBuilder();

        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }
        }

        return content.toString();
    }

    /**
     * 废弃 返回的图片路径位置错误
     * 自定义PDFTextStripper让其能返回图片文件的路径
     */
    private static class CustomPDFTextStripper extends PDFTextStripper {

        private int imageCounter = 0;
        private List<ImageInfo> imageInfos = new ArrayList<>();

        public CustomPDFTextStripper() throws IOException {
            super();
        }

        @Override
        protected void writeString(String text, List<TextPosition> textPositions) throws IOException {
            // 在这里处理文本和图片信息
            super.writeString(text, textPositions);
        }

        @Override
        protected void processTextPosition(TextPosition text) {
            // 在这里处理每个文本位置
            super.processTextPosition(text);
        }

        @Override
        protected void writePage() throws IOException {
            PDPage page = getCurrentPage();
            PDResources resources = page.getResources();

            // 收集页面中的所有图片信息
            for (COSName cosName : resources.getXObjectNames()) {
                if (resources.isImageXObject(cosName)) {
                    PDImageXObject image = (PDImageXObject) resources.getXObject(cosName);
                    ImageInfo imageInfo = new ImageInfo(imageCounter, image);
                    imageInfos.add(imageInfo);
                    imageCounter++;
                }
            }

            // 处理文本
            super.writePage();

            // 插入图片标记
            for (ImageInfo imageInfo : imageInfos) {
                output.write("[img:" + imageInfo.getImageCounter() + "]");
            }

            // 清空图片信息列表
            imageInfos.clear();
        }

        private static class ImageInfo {
            private int imageCounter;
            private PDImageXObject image;

            public ImageInfo(int imageCounter, PDImageXObject image) {
                this.imageCounter = imageCounter;
                this.image = image;
            }

            public int getImageCounter() {
                return imageCounter;
            }

            public PDImageXObject getImage() {
                return image;
            }
        }

    }

    static class ChapterInfo {
        private String title;
        private int startPage;
        private int level;

        public ChapterInfo(int startPage) {
            this.startPage = startPage;
        }

        public ChapterInfo(String title, int startPage, int level) {
            this.title = title;
            this.startPage = startPage;
            this.level = level;
        }

        public int getStartPage() {
            return startPage;
        }

        public void setStartPage(int startPage) {
            this.startPage = startPage;
        }

        public int getLevel() {
            return level;
        }

        public void setLevel(int level) {
            this.level = level;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        @Override
        public String toString() {
            return "ChapterInfo{" +
                    "title='" + title + '\'' +
                    ", startPage=" + startPage +
                    ", level=" + level +
                    '}';
        }
    }
}
